package cn.kgc.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.kgc.base.BaseInfoProperties;
import cn.kgc.entity.Users;
import cn.kgc.excpetion.GraceException;
import cn.kgc.grace.result.GraceJSONResult;
import cn.kgc.service.UsersService;
import cn.kgc.utils.JwtUtil;
import cn.kgc.vo.UsersVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import static cn.kgc.base.SystemConstant.LOGIN_TYPE_SAAS;
import static cn.kgc.base.SystemConstant.ROLE_JOB_RECRUITERS;
import static cn.kgc.grace.result.ResponseStatusEnum.*;

/**
 * @author YC
 * 二维码扫码登录模块
 */
@RestController
@RequestMapping("/saas")
@Validated
@Slf4j
@Api(tags = "二维码扫码登录模块")
public class SaasPassportController extends BaseInfoProperties {

    /**
     * 获取二维码令牌（二维码令牌本质就是字符串，由前端的组件将令牌字符串生成二维码）
     */
    @GetMapping(value = "/getQrToken")
    @ApiOperation(value = "获取二维码令牌字符串")
    public GraceJSONResult getQrToken() {
        // 生成二维码令牌
        String qrToken = UUID.randomUUID().toString();
        // 保存二维码令牌到Redis
        String key = SAAS_PLATFORM_LOGIN_TOKEN + ":" + qrToken;
        redisUtil.set(key, qrToken, 10, TimeUnit.MINUTES);
        // 保存二维码令牌的状态（设计：0未扫描 1已扫描）
        String readKey = SAAS_PLATFORM_LOGIN_TOKEN_READ + ":" + qrToken;
        redisUtil.set(readKey, "0," + qrToken, 10, TimeUnit.MINUTES);
        // 返回二维码令牌
        return GraceJSONResult.ok(qrToken);
    }

    @Resource
    private JwtUtil jwtUtil;
    @Resource
    private UsersService usersService;

    @PostMapping("/scanCode")
    @ApiOperation(value = "扫描登录二维码接口")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "qrToken", value = "二维码令牌",
                    required = true, dataType = "string", paramType = "query"),
            @ApiImplicitParam(name = "USER_APP_TOKEN", value = "app端登录令牌",
                    required = true, dataType = "string", paramType = "header")
    })
    public GraceJSONResult scanCode(String qrToken, @RequestHeader(name = "token") String appLoginToken) {
        // 判断SASS二维码令牌是否为空
        if (StrUtil.isBlank(qrToken)) {
            GraceException.display(SAAS_USER_LOGIN_TOKEN_ERROR);
        }
        // 判断APP端二维码令牌是否为空
        if (StrUtil.isBlank(appLoginToken)) {
            GraceException.display(SAAS_USER_LOGIN_APP_TOKEN_ERROR);
        }
        // 判断SASS二维码令牌是否正确
        if (!redisUtil.hasKey(SAAS_PLATFORM_LOGIN_TOKEN + ":" + qrToken)) {
            GraceException.display(SAAS_USER_LOGIN_TOKEN_ERROR);
        }
        // 判断APP端二维码令牌是否正确
        Users user = null;
        try {
            String userId = jwtUtil.getUserId(appLoginToken);
            user = usersService.getById(userId);
        } catch (Exception e) {
            GraceException.display(SAAS_USER_LOGIN_APP_TOKEN_ERROR);
        }
        if (user == null) {
            GraceException.display(SAAS_USER_NOT_EXIT_ERROR);
        }
        if (user.getRole() == ROLE_JOB_RECRUITERS) {
            GraceException.display(SAAS_USER_LOGIN_ROLE_ERROR);
        }
        // 生成预登录令牌保存到Redis中
        String preToken = UUID.randomUUID().toString();
        redisUtil.set(SAAS_PLATFORM_LOGIN_TOKEN + ":" + qrToken, preToken, 10, TimeUnit.MINUTES);
        // 标识二维码令牌已经被扫描（设计：0未扫描 1已扫描）
        redisUtil.set(SAAS_PLATFORM_LOGIN_TOKEN_READ + ":" + qrToken, "1," + preToken, 10, TimeUnit.MINUTES);
        // 删除已扫描的二维码令牌
        redisUtil.delete(SAAS_PLATFORM_LOGIN_TOKEN + ":" + qrToken);
        // 返回预登录令牌
        return GraceJSONResult.ok(preToken);
    }

    /**
     * 判断二维码令牌是否被扫描
     * 前端不停的调用这个接口来获取二维码的最新状态，设计：0未扫描 1已扫描 2已过期
     */
    @PostMapping("/codeHashBeanRead")
    @ApiOperation(value = "二维码令牌是否被扫描")
    @ApiImplicitParam(name = "qrToken", value = "二维码令牌", required = true, dataType = "String")
    public GraceJSONResult codeHashBeanRead(String qrToken) {
        List<String> list = new ArrayList<>();
        // 二维码令牌已扫描
        String readKey = SAAS_PLATFORM_LOGIN_TOKEN_READ + ":" + qrToken;
        String qrTokenFormRedis = (String) redisUtil.get(readKey);
        if (qrTokenFormRedis.startsWith("1,")) {
            list.add("1（已扫描）");
            return GraceJSONResult.ok(list);
        }
        // 二维码令牌过期
        String key = SAAS_PLATFORM_LOGIN_TOKEN + ":" + qrToken;
        if (!redisUtil.hasKey(key)) {
            list.add("2（已过期）");
            return GraceJSONResult.ok(list);
        }
        // 二维码令牌未扫描
        list.add("0（未扫描）");
        return GraceJSONResult.ok(list);
    }

    /**
     * 前端用户确认登录后调用此接口，验证令牌执行登录
     */
    @PostMapping("/goQrLogin")
    @ApiOperation(value = "用户确认登录")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "token", value = "APP端登录令牌", required = true, dataType = "String"),
            @ApiImplicitParam(name = "preToken", value = "预登录令牌", required = true, dataType = "String"),
            @ApiImplicitParam(name = "qrToken", value = "二维码令牌", required = true, dataType = "String")
    })
    public GraceJSONResult goQrLogin(@RequestHeader(name = "token") String appLoginToken, String preToken, String qrToken) {
        // 判断预登录令牌是否正确
        if (StrUtil.isBlank(preToken)) {
            GraceException.display(SAAS_USER_LOGIN_PRE_TOKEN_ERROR);
        }
        if (StrUtil.isBlank(qrToken)) {
            GraceException.display(SAAS_USER_LOGIN_TOKEN_ERROR);
        }

        String readKey = SAAS_PLATFORM_LOGIN_TOKEN_READ + ":" + qrToken;
        if (redisUtil.get(readKey) == null) {
            GraceException.display(SAAS_USER_LOGIN_ERROR);
        }
        String preTokenFormRedis = (String) redisUtil.get(readKey);
        if (!("1," + preToken).equals(preTokenFormRedis)) {
            GraceException.display(SAAS_USER_LOGIN_ERROR);
        }
        // 解析APP端登录令牌，查询用户信息
        try {
            String userId = jwtUtil.getUserId(appLoginToken);
            Users loginUser = usersService.getById(userId);
            // 将用户信息存入Redis（key：二维码令牌（页面无法获得预登录令牌），value：用户信息）
            String saasLoginUserKeyTemp = REDIS_SAAS_USER_INFO_TEMP + ":" + qrToken;
            redisUtil.set(saasLoginUserKeyTemp, loginUser, 60, TimeUnit.SECONDS);
        } catch (Exception e) {
            GraceException.display(SAAS_USER_LOGIN_APP_TOKEN_ERROR);
        }
        // 将Redis中的二维码状态删除
        redisUtil.delete(readKey);
        // 不能直接返回用户信息，因为APP登录直接返回是返回到APP端，实际应该把登录的用户信息返回到被扫码的网页前端
        return GraceJSONResult.ok();
    }

    /**
     * 校验登录状态
     */
    @PostMapping("/checkLogin")
    @ApiOperation(value = "页面定时检查是否登录")
    @ApiImplicitParam(name = "qrToken", value = "二维码令牌", required = true, dataType = "String")
    public GraceJSONResult checkLogin(String qrToken) {
        // 根据二维码令牌获取用户信息
        String userKey = REDIS_SAAS_USER_INFO_TEMP + ":" + qrToken;
        // 为空则未登录，返回无信息的响应
        if (!redisUtil.hasKey(userKey)) {
            return GraceJSONResult.ok();
        }
        // 不为空则生成页面的登录令牌，返回页面登录令牌
        Users loginUser = (Users) redisUtil.get(userKey);
        String loginToken = jwtUtil.createToken(loginUser.getId(), LOGIN_TYPE_SAAS);
        // 可选：在此接口将登录用户信息一起返回（或者单独写一个接口用于页面登录返回登录用户信息）
        UsersVO usersVO = BeanUtil.copyProperties(loginUser, UsersVO.class);
        usersVO.setToken(loginToken);
        return GraceJSONResult.ok(usersVO);
    }

}
